<?php

namespace shenyewangyue\Jt808;

use shenyewangyue\Jt808\Jt0100;
use shenyewangyue\Jt808\Jt0200;
use shenyewangyue\Jt808\Jt0002;
use shenyewangyue\Jt808\Jt0102;
use shenyewangyue\Jt808\Jt0704;

class Jt808 {
    public static $SystemOrderNumber = 0; //流水号初始值

    /**
     * description 把 ASCII 转换为十六进制
     * @param $data ASCII
     * @return mixed 16进制字符串数组
     */
    public function getTo16Bytes($asciiData) {
        // ASCII => 0x/HEX
        $hexStr = bin2hex($asciiData);

        // 粘连数据分隔 （多条数据链接在一起了）
        $hexStrArr = explode('7e7e', $hexStr);

        // 删除标识符7e/7E
        foreach($hexStrArr as $k => $v){
            $sourceStr = trim($v,"7e");
            // 反 ： 0x7d <————> 0x7d 后紧跟一个 0x01。
            $sourceStr = str_replace("7d01","7d",$sourceStr);
            // 反 ： 0x7e <————> 0x7d 后紧跟一个 0x02。
            $sourceStr = str_replace("7d02","7e",$sourceStr);
            $hexStrArr[$k] = $sourceStr;
        }

        // 字符串 转为 2个数字一组的数组
        $hexArr = [];
        foreach($hexStrArr as $k => $v){
            $hexArr[] = str_split($v, 2);
        }

        return $hexArr;
    }

    /*
     * description 把数组拼接成字符串
     * @param $data 数组 ['00','aa','02',...]
     * @return mixed 字符串
    */
    public function ArrayToString($data) {
        $str = '';
        foreach ($data as $ch) {
            $str .= $ch;
        }
        return $str;
    }
    /**
     * description  转成二进制字符串
     * @param $data array 16进制数组
     * @return string  字符串
     */
    public function getTwoStr($data) {
        //转成2进制
        $str = array();
        $req = array();
        foreach ($data as $key => $value) {
            $str[$key] = base_convert($data[$key], 16, 2);
            $leng = 8 - strlen($str[$key]);
            $req[] = $this->getTurnStr($str[$key], $leng, "0");
        }
        //拼接字符串
        $rtq = implode("", $req);
        return $rtq;
    }

    /**
     * description  把一个4位的数组转化位整形
     * @param array  接受数组
     * @return int  返回int
     */
    public function bytesToInt($data) {
        $res = [];
        foreach ($data as $k => $v) {
            $res[$k] = intval(base_convert($v, 16, 10));
        }
        $temp0 = $res[0] & 0xFF;
        $temp1 = $res[1] & 0xFF;
        $temp2 = $res[2] & 0xFF;
        $temp3 = $res[3] & 0xFF;
        return (($temp0 << 24) + ($temp1 << 16) + ($temp2 << 8) + $temp3);
    }

    /**
     * description   把一个二字节数组转化成整型
     * @param $data  二字节数组
     * @return int   整型
     */
    public function twoBytesToInteger($data) {
        $res = [];
        foreach ($data as $k => $v) {
            $res[$k] = intval(base_convert($v, 16, 10));
        }
        $temp0 = $res[0] & 0xFF;
        $temp1 = $res[1] & 0xFF;
        return (($temp0 << 8) + $temp1);
    }
    /**
     * description   把一个二字节数组转化成整型
     * @param $data  二字节数组
     * @return int   整型
     */
    public function oneBytesToInteger($data) {
        $res = [];
        foreach ($data as $k => $v) {
            $res[$k] = intval(base_convert($v, 16, 10));
        }
        $temp0 = $res[0] & 0xFF;
        return ($temp0 << 8);
    }
    /**
     * description  BCD码转字符串
     * @param array  数组
     * @return bool|string  返回字符串
     */
    public function bcdToString($data) {
        $len = count($data);
        $temp = "";
        for ($i = 0; $i < $len; $i++) {
            // 高四位
            $temp .= (($data[$i] & 0xf0) >> 4);
            // 低四位
            $temp .= ($data[$i] & 0x0f);
        }
        return (substr($temp, 0, 1) == 0) ? substr($temp, 1) : $temp;
    }

    /**
     * [arrayToBytes 返回二进制内容]
     * @author litaotxws@163.com
     * @DateTime 2020-05-01T00:34:33+0800
     * @param    [type]                   $data [数组]
     * @return   [type]                         [二进制内容]
     */
    public function arrayToBytes($data) {
        $ret = implode($data);
        return hex2bin($ret);
    }
    /**
     * [checkCode 生成验证码带开头7e]
     * @author litaotxws@163.com
     * @DateTime 2020-05-01T00:19:06+0800
     * @param    [array]                   $data [数组]
     * @return   [array]                         [返回2位数数组]
     */
    public function checkCode($data) {
        $sum = 0;
        foreach ($data as $k => $v) {
            $sum = $sum ^ hexdec($v);
        }
        return str_pad(dechex($sum), 2, "0", STR_PAD_LEFT);
    }
    public function getTurnStr($str, $n = 1, $char = '0') {
        for ($i = 0; $i < $n; $i++) {
            $str = $char . $str;
        }
        return $str;
        //return str_pad($str, $n, $char, STR_PAD_LEFT);
    }

    /******************************** 数据解析 start ******************************************/

    public function getSystemOrderArray(){
        $number = self::$SystemOrderNumber++;
        if ($number > 65025) {
            // 255 * 255 -1
            $number = 1;
        }
        $get16Number = base_convert($number, 10, 16);
        $get16Number = str_pad($get16Number, 4, "0", STR_PAD_LEFT);
        return [substr($get16Number, 0, 2) ,substr($get16Number, 2, 2) ];
    }
    /******************************** 数据解析：消息头 start ******************************************/
    /*
     * 起始字节 字段        数据类型        描述及要求
     * 0        消息ID      WORD
     * 2        消息体属性  WORD            消息体属性格式结构图见图 2
     * 4        终端手机号  BCD[6]          根据安装后终端自身的手机号转换。手机号不足 12位，则在前补充数字，大陆手机号补充数字 0，港澳台则根据其区号进行位数补充。
     * 10       消息流水号  WORD            按发送顺序从 0 开始循环累加
     * 12       消息包封装项                如果消息体属性中相关标识位确定消息分包处理，则该项有内容，否则无该项
    */
    /*
     * 解析消息头全部信息
     */
    public function getMessageHeaderArray($data){
        $messageIdNumber = $this->getMessageIdNumber($data);
        $messageType = $this->getMessageType($data);
        $equipmentNumber = $this->getEquipmentNumber($data);
        $orderNumber = $this->getMessageOrderNumber($data);
        return [
            'messageIdNumber' => $messageIdNumber,
            'messageType' => $messageType,
            'equipmentNumber' => $equipmentNumber,
            'orderNumber' => $orderNumber,
        ];
    }
    public function getMessageIdArray($data){
        $messageIdArray = array_slice($data, 0, 2);
        return $messageIdArray;
    }
    /**
     * description  获取消息ID 字符串
     * @param $data array 16进制数组
     * @param $length num 消息体长度 前位补0
     * @return bool|string   消息id字符串
     */
    public function getMessageIdNumber($data, $length = 4) {
        $messageArray = $this->getMessageIdArray($data);
        $res = [];
        foreach ($messageArray as $k => $v) {
            $res[$k] = base_convert($v, 16, 10);
        }
        $messageNumber = $this->bcdToString($res);
        return str_pad($messageNumber, $length, "0", STR_PAD_LEFT);
    }
    public function getMessageTypeArray($data){
        $messageTypeArray = array_slice($data, 2, 2);
        return $messageTypeArray;
    }
    /**
     * description 获取消息体属性
     * @param $data array 16进制数组
     * @param int $length num 消息体长度 前位补0
     * @return string 消息体属性字符串
     */
    public function getMessageType($data, $length = 4){
        $messageArray = $this->getMessageTypeArray($data);
        $res = [];
        foreach ($messageArray as $k => $v) {
            $res[$k] = base_convert($v, 16, 10);
        }
        $messageLength = '';
        foreach ($res as $k=>$v){
            $messageLength .= str_pad($v, 4, "0", STR_PAD_LEFT);
        }
        $messageLength = $messageLength + 0;
        $messageType = $this->bcdToString($res);
        $messageTypeStr = str_pad($messageType, $length, "0", STR_PAD_LEFT);
//        return $messageTypeStr;

        $messageType2 = base_convert($messageTypeStr,10,2);
        $messageType2Str = str_pad($messageType2, 16, "0", STR_PAD_LEFT);

        //[0-9]消息体长度 0000,0011,1111,1111(3ff)
        //[10-12]加密类型 0001,1100,0000,0000(1c00)
        //[13] 是否有子包 0010,0000,0000,0000(2000)(是否有子包)
        //[14-15] 保留为 1100,0000,0000,0000(C000)
        $typeStr13 = substr($messageType2Str,2,1);
        $typeStr12 = substr($messageType2Str,3,1);
        $typeStr11 = substr($messageType2Str,4,1);
        $typeStr10 = substr($messageType2Str,5,1);
        $typeStr9_0 = substr($messageType2Str,6,10);
        return [
            'encryptionType' => $typeStr10 == 1 ? 'RSA' : (($typeStr10 == '0' && $typeStr11 == '0' && $typeStr12 == '0') ? false : ''), // 加密方式 RSA 不加密 保留
            'hasSunPackage' => $typeStr13 == 1 ? "1" : "0", // 1有分包 0无分包
            'messageLength' => $messageLength,
//            'messageLength' => base_convert(base_convert(str_pad($typeStr9_0, 12, "0", STR_PAD_LEFT),2,10),16,10),
            'messageTypeStr'=>$messageTypeStr,
        ];
    }

    /**
     * description  获取16进制数组来计算出设备号
     * @param $data 16进制数组
     * @return array  返回终端手机号数组
     */
    public function getEquipmentNumberArray($data) {
        $num_array = array_slice($data, 4, 6);
        return $num_array;
    }
    /**
     * description  设备号 手机号
     * @param $data 16进制数组
     * @return array  返回终端手机号数组
     */
    public function getEquipmentNumber($data) {
        $num_array = $this->getEquipmentNumberArray($data);
        $quipmentNumber = $this->ArrayToString($num_array);
        return $quipmentNumber;
    }

    /**
     * description  获取流水号
     * @param $data array 16进制数组
     * @param int $length num 补全长度
     * @return string  返回字符串流水号
     */
    public function getMessageOrderNumberArray($data){
        $messageArray = array_slice($data, 10, 2);
        return $messageArray;
    }
    public function getMessageOrderNumber($data, $length = 4){
        $messageArray = $this->getMessageOrderNumberArray($data);
        $res = [];
        foreach ($messageArray as $k => $v) {
            $res[$k] = base_convert($v, 16, 10);
        }
        $messageOrderNumber = $this->bcdToString($res);
        return str_pad($messageOrderNumber, $length, "0", STR_PAD_LEFT);
    }

    /*********************************** 消息解析：消息头 END ********************************************/
    /*********************************** 消息解析：消息体 START ********************************************/
    public function getMessageBodyData($data){
        $messageType = $this->getMessageType($data);
        if($messageType['hasSunPackage']){
            // 消息主体
            $MessageBody = array_slice($data, 14,$messageType['messageLength']);
        }else{
            $MessageBody = array_slice($data, 12,$messageType['messageLength']);
        }
        return $MessageBody;
    }
    /*
     * description 解析消息体数据
     * @param $messageId string 0100 消息ID
     * @param $data array 16进制数组 消息体数据
     */
    public function getMessageBodyInfos($messageId,$data){
        switch ($messageId){
            case '0100': // 终端注册
                $option = new Jt0100($data);
                $retData = $option->getJt0100Data();
                break;

            case '0200': // 位置信息汇报
                $option = new Jt0200($data);
                $retData = $option->getJt0200Data();
                break;

            case '0102': // 终端鉴权
                $option = new Jt0102($data);
                $retData = $option->getJt0102Data();
                break;

            case '0002': // 终端心跳
                $option = new Jt0002($data);
                $retData = $option->getJt0002Data();
                break;

            case '0704': // 定位数据批量上传
                $option = new Jt0704($data);
                $retData = $option->getJt0704Data();
                break;

            default:
                $retData = [];
                break;
        }

        return $retData;
    }
    /*********************************** 消息解析：消息体 END ********************************************/


    /**
     * description  发送给客户端的回传数据
     * @param $data 16进制数组
     * @return string   返回客户端字符串
     */
    public function getVerifyClientDataArray($data, $auth = '313233343536'){   //31313131
        //消息ID 16进制  0x01 0x00
        $messageId = $this->getMessageIdArray($data);
//        echo 'messageId:'.json_encode($messageId)."\n\r";

        // 消息ID 字符串 0100
        $messageid = $this->getMessageIdNumber($data);
//        echo 'messageid:'.json_encode($messageid)."\n\r";

        //设备号 ["00","0f"]
        $equipmentNumber = $this->getEquipmentNumberArray($data);
        // 平台流水
        $systemNumber = $this->getSystemOrderArray();
//        echo 'systemNumber:'.json_encode($systemNumber)."\n\r";

        // 接收数据流水号 ['00','0f']
        $messageOrderArray = $this->getMessageOrderNumberArray($data);
        // 消息体
        $messageBody = [];
        $messageBody = $messageOrderArray;
        // 应答状态
        $retState = ['00'];
        // 鉴权信息数组
        $authArray = [];
        if ($messageid == '0100') { // 注册应答
            //数组开始五位
            $arrayStartFiveBytes = array('81', '00');
            if($auth){
                $authArray = str_split($auth, 2);
                $messageBody = array_merge($messageBody,$retState,$authArray);
            }
            $msglength = count(array_merge($messageOrderArray,$retState, $authArray));
        }else{
            //数组开始五位
            $arrayStartFiveBytes = array('80', '01');
            $messageBody = array_merge($messageBody, $messageId, $retState);
            $msglength = count(array_merge($messageOrderArray, $messageId, $retState));
        }
        //消息体长度
        $msglength = decbin($msglength);

        //补齐16位，不加密，无版本号
        $messageAttr = sprintf("%016d", $msglength); //消息体属性
        //前置补0
        $messageAttrStr = str_pad(dechex(bindec($messageAttr)), 4, '0', STR_PAD_LEFT);
        //分割字符
        $messageAttrArray = str_split($messageAttrStr, 2);

        // 消息头
        $messageHeader = array_merge($arrayStartFiveBytes,$messageAttrArray,$equipmentNumber,$systemNumber);

        // 校验码
        $dataAndRetXor = $this->checkCode(array_merge($messageHeader,$messageBody));

        $dataArray = array_merge(['7E'],$messageHeader,$messageBody,[$dataAndRetXor],['7E']);

        $sendClientStr = $this->arrayToBytes($dataArray);
        return $sendClientStr;
    }
    /*********************************** END ********************************************/
}